#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/io.h>
#include <linux/string.h>

#include "led_operation.h"

#define DEVICE_NAME "imx_led_dev"

static int major;
static struct class *led_class;
static struct led_operation *led_opt;


ssize_t led_read(struct file *fd, char __user *buf, 
    size_t size, loff_t *lof){
        return 0;
}

ssize_t led_write(struct file *file, const char __user *buf, 
    size_t size, loff_t *lof){
    char cmd;  
    int err;

    struct inode *node = file_inode(file); 
    int minor = iminor(node);

    err = copy_from_user(&cmd, buf, 1);
    if(err){
        pr_err("Failed to copy data from user space (error: %d)\n", err);
        return -EFAULT;
    }
    printk(KERN_INFO "command from userspace: '%c'\n", cmd);
    
    led_opt->ctrl(minor, cmd);

    return 1;  
}

int led_open(struct inode *node, struct file *fd){
    
    int minor = iminor(node);

    led_opt->init(minor);

    printk(KERN_INFO "led driver initialized\n");
    return 0;
}

int led_close(struct inode *node, struct file *fd){
    return 0;
}

static struct file_operations led_fop = {
    .owner   = THIS_MODULE,
	.read    = led_read,
	.write   = led_write,
    .open    = led_open,
    .release = led_close,
};

static int __init led_init(void) {
    int ret = 0;

    major = register_chrdev(0, DEVICE_NAME, &led_fop);
    if (major < 0) {
        ret = major;
        printk(KERN_ERR "led_driver: Failed to register char device, error %d\n", ret);
        return ret;
    }

    led_class = class_create(THIS_MODULE, DEVICE_NAME);
    if (IS_ERR(led_class)) {
        ret = PTR_ERR(led_class);
        printk(KERN_ERR "led_driver: Failed to create class, error %d\n", ret);
        unregister_chrdev(major, DEVICE_NAME);
        return ret;
    }

    int i ;
    led_opt = get_board_led_optr();
    for(i = 0; i < led_opt->num; i++){
        if (!device_create(led_class, NULL, MKDEV(major, i), NULL, "%s%d",DEVICE_NAME,i)) {
            ret = -ENODEV;
            printk(KERN_ERR "led_driver: Failed to create device\n");
            class_destroy(led_class);
            unregister_chrdev(major, DEVICE_NAME);
            return ret;
        }
    }

    printk(KERN_INFO "led_driver: sucessed to create device\n");
    return 0;
}

static void __exit led_exit(void){
    led_opt->exit();
    int i ;
    for(i= 0; i < led_opt->num; i++)
        device_destroy(led_class, MKDEV(major, i));

    class_destroy(led_class);
    unregister_chrdev(major, DEVICE_NAME);

    printk(KERN_INFO "device %s(%d) unregisted\n", DEVICE_NAME, major);
}

module_init(led_init);
module_exit(led_exit);

MODULE_AUTHOR("fison");
MODULE_DESCRIPTION("led device for imx6ull");
MODULE_LICENSE("GPL");